require "cjson"
require "timer"
require "gpio"
require "pwm"
require "uart"
require "dnalua"

local global_seq = 0
local boot_ntf_retry_cnt = 0
local boot_ntf_retry_pkg = {}
local net_ntf_retry_cnt = 0
local net_ntf_retry_pkg = {}

function DNAGpioInit()

end

function DNAUartInit()
    local conf = {}
    conf["id"] = 0
    conf["baud"] = 9600
    conf["bits"] = 8
    conf["parity"] = "none"
    conf["stop"] = 1
    conf["fit"] = 40
    return conf
end

function DNADevinfoInit()
    local conf = {}
    conf["platform"] = "8710"
    conf["name"] = "dnalua"
    conf["devtype"] = 24414
    conf["pid"] = "0000000000000000000000005e5f0000"
    conf["cfgtype"] = 0
    conf["smtcfg_timeout"] = 0
    conf["hostname"] = "WiFi_Device"
    conf["ap"] = "BroadLink_WiFi_Device-"
    return conf
end

function DNACloudsInit()
    -- local cldcfg = {}
    local conf = {}
    conf["clds"] = {"bl","hi"}
    -- conf["valuetype"] = {0,0,0,0}
    -- -- bl cloud config
    -- cldcfg = {}
    -- cldcfg["keymap"] = {"pwr","light","light+","light-"}
    -- conf["bl"] = cldcfg
    -- -- hi cloud config
    -- cldcfg = {}
    -- cldcfg["keymap"] = {"power","brightness","brightness+","brightness-"}
    -- conf["hi"] = cldcfg
    return conf
end

function create_frame(payload)
    local t = {0xbe, 0xbe, 0xbe, 0xbe, 0x02, 0x01}
    -- append payload size
    local size = #payload
    table.insert(t, size & 0x00ff)
    table.insert(t, (size >> 8) & 0x00ff)
    -- append crc
    table.insert(t, 0x00)
    table.insert(t, 0x00)
    -- append payload
    for i,v in ipairs(payload) do
        -- print("["..i.."] v:"..v)
        table.insert(t, v)
    end
    -- append tail
    table.insert(t, 0xed)
    table.insert(t, 0xed)
    return t
end

function create_req(cmdid, seq, data)
    local t = {}
    -- append cmdid
    table.insert(t, cmdid & 0x00ff)
    table.insert(t, (cmdid >> 8) & 0x00ff)
    -- append seq
    table.insert(t, seq & 0x00ff)
    table.insert(t, (seq >> 8) & 0x00ff)
    -- append data
    for i,v in ipairs(data) do
        -- print("["..i.."] v:"..v)
        table.insert(t, v)
    end
    return t
end

function create_rsp(cmdid, seq, opcode, data)
    local t = {}
    -- append cmdid
    table.insert(t, cmdid & 0x00ff)
    table.insert(t, (cmdid >> 8) & 0x00ff)
    -- append seq
    table.insert(t, seq & 0x00ff)
    table.insert(t, (seq >> 8) & 0x00ff)
    -- append opcode
    table.insert(t, opcode)
    -- append data
    for i,v in ipairs(data) do
        -- print("["..i.."] v:"..v)
        table.insert(t, v)
    end
    return t
end

function port_test_rsp(seq)
    local t = {0x02, 0xff, 0x00}
    uart.write(create_frame(create_rsp(0x8012, seq, 0x00, t)))
end

function tmr_reboot()
    timer.delete("tmr_reboot")
    dnalua.reboot()
end

function reboot_rsp(seq)
    timer.new("tmr_reboot", 1000)
    local t = {0x32, 0x22, 0x00}
    uart.write(create_frame(create_rsp(0x8012, seq, 0x00, t)))
end

function onoff_rsp(seq)
    local t = {0x12, 0x22, 0x00}
    return create_frame(create_rsp(0x8012, seq, 0x00, t))
end

function boot_ntf_tmr()
    boot_ntf_retry_cnt = boot_ntf_retry_cnt + 1
    if boot_ntf_retry_cnt > 3 then
        timer.delete("boot_ntf_tmr")
        boot_ntf_retry_cnt = 0
    end

    uart.write(boot_ntf_retry_pkg)
end

function DNAStartup(mode)
    global_seq = global_seq + 1
    local t = {0x15, 0x20, 0x73, 0x01, mode}
    boot_ntf_retry_pkg = create_frame(create_req(0x8005, global_seq, t))
    uart.write(boot_ntf_retry_pkg)

    boot_ntf_retry_cnt = 1
    timer.new("boot_ntf_tmr", 300)
end

function net_ntf_tmr()
    net_ntf_retry_cnt = net_ntf_retry_cnt + 1
    if net_ntf_retry_cnt > 3 then
        timer.delete("net_ntf_tmr")
        net_ntf_retry_cnt = 0
    end

    uart.write(net_ntf_retry_pkg)
end

function DNANetwork(status)
    if net_ntf_retry_cnt ~= 0 then -- now working on
        timer.delete("net_ntf_tmr")
        net_ntf_retry_cnt = 0
    end

    global_seq = global_seq + 1
    local t = {0x15, 0x20, 0x72, 0x02, status, dnalua.rssi()}
    net_ntf_retry_pkg = create_frame(create_req(0x8005, global_seq, t))
    uart.write(net_ntf_retry_pkg)

    net_ntf_retry_cnt = 1
    timer.new("net_ntf_tmr", 300)
end

function prop_get_rsp(seq, num, prop)
    local t = {0x12, 0x20, 0x00}
    table.insert(t, num) -- append prop num
    for i,v in ipairs(prop) do
        if v == 0x74 then -- append system
            table.insert(t, 0x74)
            table.insert(t, 0x04)
            local time = dnalua.uptime()
            table.insert(t, time & 0x000000ff)
            table.insert(t, (time >> 8) & 0x000000ff)
            table.insert(t, (time >> 16) & 0x000000ff)
            table.insert(t, (time >> 24) & 0x000000ff)
        elseif v == 0x75 then
            table.insert(t, 0x75)
            table.insert(t, 0x06)
            local mac = dnalua.get_mac()
            for m,n in ipairs(mac) do
                table.insert(t, n)
            end
        end
    end
    uart.write(create_frame(create_rsp(0x8006, seq, 0x00, t)))
end

function onoff_dev_hdl(cloudid, seq, onoff, snap)

    local t = {0x12, 0x22, 0x00}
    uart.write(create_frame(create_rsp(0x8012, seq, 0x00, t)))

    -- update snapshot
    t = cjson.decode(snap)
    t["pwr"] = onoff
    snap = cjson.encode(t)

    dnalua.cloud_upload(dnalua.PORT_ID_BL, dnalua.CLOUD_ACTIVE_UPLOAD, dnalua.CLOUD_DEVDATA_KEYVALUE, snap)

    return snap
end

function dev_onoff_rsp(cloudid, kvin, snap)
    local t = {}
    local kvout = nil

    t = cjson.decode(snap)

    if cloudid == dnalua.PORT_ID_BL then
        local bl = cjson.decode(kvin)
        t["pwr"] = bl["pwr"]
        snap = cjson.encode(t) -- resp current snap to cloud
        kvout = snap
    end

    if cloudid == dnalua.PORT_ID_HI then
        local hi = cjson.decode(kvin)
        t["pwr"] = hi["power"]
        snap = cjson.encode(t) -- resp current snap to cloud

        hi = {}
        hi["power"] = t["pwr"]
        kvout = cjson.encode(hi)
    end

    return kvout, snap
end

function DNABin2KV(cloudid, binin, kvin, snapshot)
    local kvout = nil

    if (binin == nil) then
        -- print("binin nil snapshot "..snapshot)
    else
        -- print("binin "..#binin.." snapshot "..snapshot)
        if binin[1] ~= 0xbe or binin[2] ~= 0xbe or binin[3] ~= 0xbe or binin[4] ~= 0xbe or binin[5] ~= 0x02 or binin[6] ~= 0x01 then
            -- print("binin err")
        else
            paylsize = (binin[8] << 8) | binin[7]
            if #binin < (paylsize + 12) then
                -- print("binin size "..#binin.." err paylsize "..paylsize)
            else
                local cmdid = (binin[12] << 8) | binin[11]
                local seq = (binin[14] << 8) | binin[13]
                global_seq = seq -- update global seq

                local propid = 0
                local opcode = 0
                local actionid = 0

                if cmdid == 0x8005 then

                    propid = (binin[16] << 8) | binin[15]

                    if propid == 0x2011 then
                        local p = {}
                        for i=1,(binin[17]) do
                            table.insert(p, #p+1, binin[17+i])
                        end
                        prop_get_rsp(seq, binin[17], p)
                    end

                elseif cmdid == 0x8006 then -- this is response, have opcode

                    opcode = binin[15]
                    propid = (binin[17] << 8) | binin[16]

                    if propid == 0x2016 then
                        if boot_ntf_retry_cnt ~= 0 then
                            timer.delete("boot_ntf_tmr")
                            boot_ntf_retry_cnt = 0
                        elseif net_ntf_retry_cnt ~= 0 then
                            timer.delete("net_ntf_tmr")
                            boot_ntf_retry_cnt = 0
                        end
                    end

                elseif cmdid == 0x8011 then

                    actionid = (binin[16] << 8) | binin[15]

                    if actionid == 0xff01 then
                        port_test_rsp(seq)
                    elseif actionid == 0x2231 then
                        reboot_rsp(seq)
                    elseif actionid == 0x2211 then
                        snapshot = onoff_dev_hdl(cloudid, seq, binin[17], snapshot)
                    end

                elseif cmdid == 0x8012 then

                    opcode = binin[15]
                    actionid = (binin[17] << 8) | binin[16]

                    if actionid == 0x2212 then
                        if binin[18] == 0x0 then -- success
                            kvout, snapshot = dev_onoff_rsp(cloudid, kvin, snapshot)
                        end
                    end

                elseif cmdid == 0x8012 then

                end
            end
        end
    end

    return kvout, snapshot
end

function cloud_onoff_req(onoff)
    local t = {0x11, 0x22, onoff}
    global_seq = global_seq + 1
    uart.write(create_frame(create_req(0x8011, global_seq, t)))
end

function DNAKV2Bin(cloudid, kvin, snapshot)
    local kvout = nil

    if (kvin == nil) then
        print("[GET] cloudid "..cloudid.." snapshot "..snapshot)
        kvout = snapshot
    else
        print("[SET] cloudid "..cloudid.." inlen "..#kvin.." snapshot "..snapshot)
        local s = cjson.decode(kvin)
        if s["pwr"] == nil then
            kvout = snapshot
        else
            cloud_onoff_req(s["pwr"])
        end
    end

    return kvout, snapshot
end